#!/usr/bin/env python

# example helloworld.py

import pygtk
pygtk.require('2.0')
import gtk, gobject
from ftplib import FTP
import os,stat,ftplib
import thread

class Error(Exception): pass
class error_perm(Error): pass           # 5xx errors

class Ftpete:
    remotecolumn_names = ['Name', 'Size', 'Mode', 'Last Changed']
    downloading = {'filename' : '', 'size': 0, 'obj': None,'progress' : 0,'isbinary':False}
    loginstatus = 0
    downloadqueue = []
    indownload = False
    downloaded = 0
    currentdownload = None
    fileexistsaction = 0
    
    def connect(self):
        self.mainFtp = FTP(self.entryServer.get_text())
        x = self.mainFtp.set_debuglevel(0)
        self.mainFtp.login(self.entryUser.get_text(),self.entryPasswd.get_text())
        self.getCurrentPath()
        self.listDir()
        #thread.start_new_thread(self.thread_connect,())

    def click_connect(self, widget, data=None):
        thread.start_new_thread(self.connect,())
        

    def getCurrentPath(self):
        self.remotecurrentPath = self.mainFtp.pwd()
        self.entryCwd.set_text(self.remotecurrentPath)

    def listDir(self,directory = '',UpdateTree = True):
        self.dictRemoteFIles = {}
        self.boolStartingList = True
        if UpdateTree:
            self.mainFtp.dir(directory,self.listDirCB)
        else:
            self.mainFtp.dir(directory,self.listDirForDld)
    
    def listDirForDld(self,line):
        list = line.split()
        
    def listDirCB(self,line):
        if self.boolStartingList == True:
            self.boolStartingList = False
            self.treeviewRemote.get_model().clear()
        list = line.split()
        #print line
        if list[8] != '.':
            if list[0][0] == "d" or list[0][0] == "l":
                bIsDir = True
                sortname = "0_" + list[8]
                showname = "(dir) " + list[8]
            else:
                bIsDir = False
                sortname = "1_" + list[8]
                showname = list[8]
            self.dictRemoteFIles[list[8]] = {'name' : list}
            #print "adding " + showname
            retrow = self.treeviewRemote.get_model().insert_after(None, [showname,bIsDir,sortname, list[8],list[4]])
            #self.treeremoteList.set_value(retrow,0,showname)
        self.treeviewRemote.set_model(self.treeremoteList)
        
    def setPath(self,widget,data = None):
        path = self.entryCwd.get_text()
        path = path.replace('\\','/')
        if path == '':
            path = '/'
        self.remotecurrentPath = path
        errorListing = False
        try:
            self.mainFtp.cwd(path)
        except ftplib.error_perm:
            self.boolStartingList = False
            errorListing = True
        if errorListing == False:
            self.remotecurrentPath = self.mainFtp.pwd()
            self.listDir(self.remotecurrentPath)
        else:
            newpath = self.remotecurrentPath
            tmppath = newpath.split('/')
            del tmppath[-1]
            newpath = '/'.join(tmppath)
            self.remotecurrentPath = newpath
            self.entryCwd.set_text(newpath)
            
    def treevremoteClick(self,widget,path = None,column = None,param = None):
        model = widget.get_model()
        iter = model.get_iter(path)
        data = model.get(iter,3,1)
        if data[1]:
            newpath = self.remotecurrentPath
            if data[0] == '..':
                tmppath = newpath.split('/')
                del tmppath[-1]
                newpath = '/'.join(tmppath)
            else:
                if newpath[-1] == '/':
                    newpath = newpath + data[0]
                else:
                    newpath = newpath + '/' +  data[0]
            self.entryCwd.set_text(newpath)
            self.setPath(self,widget)
        
    def setLocalPath(self,widget,data = None):
        path = self.entryLocalDir.get_text()
        path = path.replace('\\','/')
        if path == '':
            path = '/home/javier'
        errorListing = False
        self.browseLocal(path)
        
    def browseLocal(self,path = ''):
        if path == '':
            path = '/'
        bFolderExists = False
        try:
                folderstat = os.stat(path)
                if stat.S_ISDIR(folderstat.st_mode):
                    bFolderExists = True
        except OSError:
                bFolderExists = False
                self.entryLocalDir.set_text(self.localcurrentPath)
                print "Folder does not exist"
        if bFolderExists:
            boolCantBrowse = False
            try:
                listFiles = os.listdir(path)
            except OSError:
                boolCantBrowse = True
                print "Permision denied"
            if boolCantBrowse == False:
                self.treelocallist.clear()
                self.localcurrentPath = path
                files = []
                for f in listFiles:
                    if f[0] != '.':
                        files = files + [f]
                self.entryLocalDir.set_text(path)
                files.sort()
                files = ['..'] + files
                for f in files:
                    bFileDont = False
                    filename = os.path.join(path, f)
                    try:
                            filestat = os.stat(filename)
                    except OSError:
                            bFileDont = True
                            print "Can't read file?"
                    if bFileDont == False:
                        realname = f
                        if stat.S_ISDIR(filestat.st_mode):
                            bIsDir = True
                            sortname = "0" + f
                            f = "(dir) " + f
                        else:
                            bIsDir = False
                            sortname = "1" + f
                        retrow = self.treelocallist.append([f,bIsDir,sortname, realname])

    def treevlocalClick(self,widget,path = None,column = None,param = None):
        model = widget.get_model()
        iter = model.get_iter(path)
        data = model.get(iter,3,1)
        if data[1]:
            newpath = self.localcurrentPath
            if data[0] == '..':
                tmppath = newpath.split('/')
                del tmppath[-1]
                newpath = '/'.join(tmppath)
            else:
                if newpath[-1] == '/':
                    newpath = newpath + data[0]
                else:
                    newpath = newpath + '/' +  data[0]
            
            self.browseLocal(newpath)
            
    def clickDownload(self,widget,data = None):
        sel = self.treeviewRemote.get_selection()
        selr = sel.get_selected_rows()
        model = selr[0]
        selected = selr[1]
        for path in selected:
            iter = model.get_iter(path)
            data = model.get(iter,3,1,4)
            
            if(data[1]): #download fucking dir (some day, maaaybe =( )
                currentpath = self.mainFtp.pwd()
                if not currentpath.endswith('/'):# == False:
                    currentpath = currentpath + '/'
                currentpath = currentpath + data[3]
                
                #self.mainFtp.cwd(
            else:
                dlditem = self.addToQueue(data[0],self.remotecurrentPath,data[2],True)
                if not self.indownload:
                    tup = (dlditem,0)
                    if True: #False means you're debugging and don't want this thread to go away
                        thread.start_new_thread(self.dldqueueManager,(tup))
                    else:
                        self.dldqueueManager(dlditem,0)
                    
            #break

    def clickUpload(self,widget,data = None):
        sel = self.treeviewLocal.get_selection()
        selr = sel.get_selected_rows()
        model = selr[0]
        selected = selr[1]
        for path in selected:
            iter = model.get_iter(path)
            data = model.get(iter,3,1)
            #([f,bIsDir,sortname, realname])
            if data[1]: #upload fucking dir (some day, maaaybe =( )
                currentpath = self.mainFtp.pwd()
                if not currentpath.endswith('/'):# == False:
                    currentpath = currentpath + '/'
                currentpath = currentpath + data[3]
                
                #self.mainFtp.cwd(
            else:
                dlditem = self.addToQueue(data[0],self.remotecurrentPath,data[2],True)
                if not self.indownload:
                    tup = (dlditem,0)
                    if True: #False means you're debugging and don't want this thread to go away
                        thread.start_new_thread(self.dldqueueManager,(tup))
                    else:
                        self.dldqueueManager(dlditem,0)
                    
            #break
    
    def addToQueue(self, file,path,size,isBinary):
        if not path.endswith('/'):# == False:
            path = path + '/'
        fullfile = path + file
        
        dict = {}
        dict['file'] = file
        dict['path'] = path
        dict['fullfile'] = fullfile
        dict['size'] = size
        dict['downloading'] = False
        dict['isbinary'] = isBinary
        dict['localfile'] = ''
        dict['treeiter'] = None
        dict['fileobj'] = None
        dict['fatalerror'] = False
        
        self.downloadqueue.append(dict)
        item = self.downloadqueue[len(self.downloadqueue)-1]
        retrow = self.treeQueueList.append(['dld',item['fullfile'],'0%',item['size'],'waiting',item])
        item['treeiter'] = retrow
        self.treeviewRemote.set_model(self.treeremoteList)
        self.treeviewRemote.scroll_to_cell(self.treeQueueList.get_path(retrow))
        return item
        
    def dldqueueManager(self,dlditem,moveonnothingtoseehere):
        x = 1
        if not self.indownload:
            for item in self.downloadqueue:
                if not item['fatalerror']:
                    self.download_start(item,0)
    
    def get_QueueNotFailedCount(self):
        cnt = 0
        for item in self.downloadqueue:
            if not item['fatalerror']:
                cnt = cnt+1
        return cnt
    

    
    def download_start(self,dlditem,moveonnothingtoseehere):
        bFileOpened = False
        self.indownload = True
        downloadfile = self.localcurrentPath +'/' + dlditem['file']
        if self.fileexistsaction == 0:
            fileexretr = self.local_file_exists(downloadfile)
        else:
            fileexretr = self.fileexistsaction
        
        if fileexretr == 1: #overwrite
            doresume = False
        elif fileexretr == 2:
            self.treeQueueList.set_value(dlditem['treeiter'],4,"File skipped/cancelled")
            dlditem['fatalerror'] = True
            self.indownload = False
            self.treeviewRemote.set_model(self.treeremoteList)
            return
        elif fileexretr == 3:
            doresume = True #falta el resume
            
        try:
            dlditem['fileobj'] = open(downloadfile,'w')
            bFileOpened = True
        except IOError:
            bFileOpened = False
        if bFileOpened: 
            self.currentdownload = dlditem
            self.treeQueueList.set_value(dlditem['treeiter'],4,"starting download...")
            self.downloaded = 0
            dlditem['downloading'] = True
            if dlditem['isbinary']:
                self.mainFtp.retrbinary('RETR ' +dlditem['fullfile'],self.downloadCallback)
            else:
                self.mainFtp.retrlines('RETR ' +dlditem['fullfile'],self.downloadCallback)
            
            dlditem['fileobj'].close()
            self.indownload = False
            self.treeQueueList.remove(dlditem['treeiter'])
            dlditem['downloading'] = False
            self.downloadqueue.remove(dlditem)
            self.indownload = False
        else:
            self.treeQueueList.set_value(dlditem['treeiter'],4,"Local error, can't open file")
            dlditem['fatalerror'] = True
        self.indownload = False
        
    def downloadCallback(self,data):
        self.treeQueueList.set_value(self.currentdownload['treeiter'],4,"downloading...")
        if not self.currentdownload['isbinary']:
            data = data + "\n"
        self.downloaded = self.downloaded+ len(data)
        newpercent = (self.downloaded * 100) / int(self.currentdownload['size'])        
        self.treeQueueList.set_value(self.currentdownload['treeiter'],2,str(newpercent) + "%")
        self.currentdownload['fileobj'].write(data)
        
    def delete_event(self, widget, event, data=None):
        # If you return FALSE in the "delete_event" signal handler,
        # GTK will emit the "destroy" signal. Returning TRUE means
        # you don't want the window to be destroyed.
        # This is useful for popping up 'are you sure you want to quit?'
        # type dialogs.
        print "delete event occurred"

        # Change FALSE to TRUE and the main window will not be destroyed
        # with a "delete_event".
        return False

    def destroy(self, widget, data=None):
        print "destroy signal occurred"
        gtk.main_quit()

    def __init__(self):
        # create a new window
        self.window = gtk.Window(gtk.WINDOW_TOPLEVEL)
    
        # When the window is given the "delete_event" signal (this is given
        # by the window manager, usually by the "close" option, or on the
        # titlebar), we ask it to call the delete_event () function
        # as defined above. The data passed to the callback
        # function is NULL and is ignored in the callback function.
        self.window.connect("delete_event", self.delete_event)
    
        # Here we connect the "destroy" event to a signal handler.  
        # This event occurs when we call gtk_widget_destroy() on the window,
        # or if we return FALSE in the "delete_event" callback.
        self.window.connect("destroy", self.destroy)
    
        # Sets the border width of the window.
        self.window.set_border_width(10)
        ####################
        mainbox = gtk.VBox()
        
        #start quickconnect box
        tmpbox = gtk.HBox(False,)
        label = gtk.Label("Server:")
        tmpbox.pack_start(label,expand=False,fill=False)
        self.entryServer = gtk.Entry()
        tmpbox.pack_start(self.entryServer,expand=False,fill=False)

        label = gtk.Label("User:")
        tmpbox.pack_start(label,expand=False,fill=False)
        self.entryUser = gtk.Entry()
        tmpbox.pack_start(self.entryUser,expand=False,fill=False)

        label = gtk.Label("Password:")
        tmpbox.pack_start(label,expand=False,fill=False)
        self.entryPasswd = gtk.Entry()
        tmpbox.pack_start(self.entryPasswd,expand=False,fill=False)

        
        buttonConnect = gtk.Button("Connect")
        tmpbox.pack_start(buttonConnect,fill=True)
        buttonConnect.connect("clicked", self.click_connect, None)
        buttonConnect.show()
                
        tmpbox.show()
               
        mainbox.pack_start(tmpbox,expand=False,fill=False)
        #end quickconenct
        
        #start browsing box
        tmpbox = gtk.HBox()
        
        #start local box
        tmpVbox = gtk.VBox()
        #start location box
        tmpbox1 = gtk.HBox(False)
        self.entryLocalDir = gtk.Entry()
        tmpbox1.pack_start(self.entryLocalDir,True)
        self.buttonSetLocal = gtk.Button("Go")
        self.buttonSetLocal.connect("clicked", self.setLocalPath, None)
        tmpbox1.pack_start(self.buttonSetLocal,False,False)
        #end location box
        tmpVbox.pack_start(tmpbox1,False)
        #start local tree
        self.treeviewLocal = gtk.TreeView()
        self.treeviewLocal.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        self.treelocallist = gtk.ListStore(str,bool,str,str)
        self.treelocallist.set_sort_column_id(2,gtk.SORT_ASCENDING)
        self.treeviewLocal.set_model(self.treelocallist)
        self.treeviewLocal.insert_column_with_attributes(0, "File", gtk.CellRendererText(), text=0)
        self.treeviewLocal.connect("row-activated", self.treevlocalClick, None)
        
        self.scrolledWinRemote = gtk.ScrolledWindow()
        self.scrolledWinRemote.show()
        self.scrolledWinRemote.add(self.treeviewLocal)
        tmpVbox.pack_start(self.scrolledWinRemote,True)
        #end local tree
        tmpbox.pack_start(tmpVbox,True)
        #end local box
        
        #start center buttons
        tmpcenterbox = gtk.VBox()
        self.buttonUpload = gtk.Button("^")
        self.buttonUpload.connect("clicked", self.clickUpload, None)
        self.buttonDownload = gtk.Button("V")
        self.buttonDownload.connect("clicked", self.clickDownload, None)
        tmpcenterbox.pack_start(self.buttonUpload,True,False)
        tmpcenterbox.pack_start(self.buttonDownload,True,False)
        tmpbox.pack_start(tmpcenterbox,False,False)
        #end center buttons
        
        tmpVbox = gtk.VBox()
        tmpbox1 = gtk.HBox(False,)    
        self.entryCwd = gtk.Entry()
        tmpbox1.pack_start(self.entryCwd,True,True)
        self.entryCwd.show()
        buttonCwd = gtk.Button("Cwd to")
        tmpbox1.pack_start(buttonCwd,False)
        buttonCwd.connect("clicked", self.setPath, None)
        buttonCwd.show()
        tmpbox1.show()
        tmpVbox.pack_start(tmpbox1,False)
        
        self.treeviewRemote = gtk.TreeView()
        self.treeremoteList = gtk.ListStore(str,bool,str,str,str)
        self.treeremoteList.set_sort_column_id(2,gtk.SORT_ASCENDING)
        self.treeviewRemote.set_model(self.treeremoteList)
        self.treeviewRemote.get_selection().set_mode(gtk.SELECTION_MULTIPLE)
        self.treeviewRemote.insert_column_with_attributes(0, "File", gtk.CellRendererText(), text=0)
        self.treeviewRemote.connect("row-activated", self.treevremoteClick, None)
        
        #
        #self.tvcolumn = [None] * len(self.remotecolumn_names)
        #cell = gtk.CellRendererText()
        #self.tvcolumn[0] = gtk.TreeViewColumn(self.remotecolumn_names[0], cell)
        #self.tvcolumn[0].set_cell_data_func(cell, "hola")
        #cell = gtk.CellRendererText()
        #self.tvcolumn[0].pack_start(cell, False)
        #self.tvcolumn[0].set_cell_data_func(cell, "eh")
        #self.treeviewRemote.append_column(self.tvcolumn[0])
        #
        
        # add scrollbox with treeview
        self.scrolledWinRemote = gtk.ScrolledWindow()
        self.scrolledWinRemote.show()
        tmpVbox.add(self.scrolledWinRemote)
        self.scrolledWinRemote.add(self.treeviewRemote)
        
        tmpVbox.show()
        tmpbox.add(tmpVbox)
        tmpbox.show()
        mainbox.add(tmpbox)
        #end browsing box
        
        #start Queue manager box
        tmpvox = gtk.HBox()
        self.treeQueue = gtk.TreeView()
        self.treeQueueList = gtk.ListStore(str,str,str,str,str,gobject.TYPE_PYOBJECT)
        self.treeQueue.set_model(self.treeQueueList)
        self.treeQueue.insert_column_with_attributes(0, "Ico", gtk.CellRendererText(), text=0)
        self.treeQueue.insert_column_with_attributes(1, "File", gtk.CellRendererText(), text=1)
        self.treeQueue.insert_column_with_attributes(2, "Progress", gtk.CellRendererText(), text=2)
        self.treeQueue.insert_column_with_attributes(3, "Size", gtk.CellRendererText(), text=3)
        self.treeQueue.insert_column_with_attributes(4, "Status", gtk.CellRendererText(), text=4)
        #self.treeviewRemote.connect("row-activated", self.treevremoteClick, None)
        tmpvox.pack_start(self.treeQueue)
        mainbox.pack_start(tmpvox)
        #end queue manager box
        
        #start log box
        tmpbox = gtk.HBox()
        self.textviewLog = gtk.TextView()
        tmpbox.pack_start(self.textviewLog,True)
        self.textviewLog.show()
        
        tmpbox.show()
        
        #mainbox.pack_start(tmpbox,True,True)
        #end log box
        mainbox.show()
        
        self.window.add(mainbox)
        self.window.set_default_size(600,700)
        self.window.show_all()
        self.browseLocal('/home/javier')
        
        
    def local_file_exists(self,file):
        try:
                about = os.stat(file)
                fileexists = True;
        except OSError:
                fileexists = False;
        if fileexists:
            self.dialog = gtk.Dialog("File already exists", self.window, 0,
                                     (gtk.STOCK_CLOSE, gtk.RESPONSE_CLOSE))
            self.dialog.set_default_size(300, 225)
            
            gtk.gdk.threads_enter()
            lbl = gtk.Label('The file you\'re trying to download already exists.\nWhat should i do?')
            lbl.set_justify(gtk.JUSTIFY_LEFT)
            self.dialog.vbox.pack_start(lbl)
            rb = gtk.RadioButton(None,"Overwrite local file")
            self.dialog.vbox.pack_start(rb)
            rb2 = gtk.RadioButton(rb,"Skip/cancel this download")
            self.dialog.vbox.pack_start(rb2)
            rb3 = gtk.RadioButton(rb,"Resume previous download")
            self.dialog.vbox.pack_start(rb3)
            self.dialog.vbox.pack_start(gtk.HSeparator())
            checkb = gtk.CheckButton('Don\'t ask me again this session', True)
            self.dialog.vbox.pack_start(checkb)
            
            self.dialog.show_all()
            self.dialog.run()
            self.dialog.hide()
            gtk.gdk.threads_leave()
            
            if rb.get_active():
                answer = 1
                pass
            elif rb2.get_active():
                answer = 2
                pass
            elif rb3.get_active():
                answer = 3
                pass
                
            if checkb.get_active():
                self.fileexistsaction = answer
            
            return answer
        else:
            return 1
        
    def main(self):
        # All PyGTK applications must have a gtk.main(). Control ends here
        # and waits for an event to occur (like a key press or mouse event).
        #self.connect(self)
        self.entryServer.set_text('www.la-redo.com.ar')
        self.entryUser.set_text('laredo')
        self.entryPasswd.set_text('mancuerna')
        
#        self.entryServer.set_text('dev.sharpersites.com')
#        self.entryUser.set_text('internal')
#        self.entryPasswd.set_text('sharpill')
        
#        thread.start_new_thread(self.connect,())
        gtk.threads_init() 
        gtk.main()

# If the program is run directly or passed as an argument to the python
# interpreter then create a HelloWorld instance and show it
if __name__ == "__main__":
    ftpete = Ftpete()
    ftpete.main()
    
